package scenic.toolkit.db;

import scenic.toolkit.db.event.ArrayInsertEvent;
import scenic.toolkit.db.event.ArrayRemoveEvent;
import scenic.toolkit.db.event.ArrayWriteEvent;

public abstract class Array extends Node {
	private int size;
	private ArrayType type;
	
	public Array(ArrayType type) {
		this.type = type;
	}
	
	public Node getNode() {
		return this;
	}
	
	public ArrayType getType() {
		return type;
	}
		
	public void setSize(int newSize) {
		Object newData = allocate(newSize);
		if (getData() != null)
			System.arraycopy(getData(), 0, newData, 0, Math.min(size, newSize));
		size = newSize;
		setData(newData);
	}
	
	public int getSize() {
		return size;
	}
	
	public int getLength() {
		return size;
	}
	
	protected abstract Object getData();
	
	protected abstract void setData(Object data);
	
	protected abstract Object allocate(int size);
	
	protected void write(int position, Object array, int length) throws IndexOutOfBoundsException {
		if(position < 0 || position + length > size)
			throw new IndexOutOfBoundsException();
		System.arraycopy(array, 0, getData(), position, length);
		notifyChange(new ArrayWriteEvent(this, position, size));
	}
	
	protected void insert(int position, Object array, int length) {
		if(position < 0 || position > size)
			throw new IndexOutOfBoundsException();
		int newSize = size + length;
		Object newData = allocate(newSize);
		
		if(getData() != null) {
			System.arraycopy(getData(), 0, newData, 0, position);
			System.arraycopy(getData(), position, newData, position + length, 
					size - position);
		}
		System.arraycopy(array, 0, newData, position, length);
		size = newSize;
		setData(newData);
		notifyInsert(position, length);
	}

	public void remove(int position, int length) {
		if(position < 0 || position + length > this.size)
			throw new IndexOutOfBoundsException();
		System.arraycopy(getData(), position + length, getData(), position, 
				size - (position + length));
		size -= length;
		notifyRemove(position, length);
	}
	
	public void move(int src, int dest, int length) {
		if(src < 0 || src + length > this.size 
				|| dest < 0 || dest + length > this.size )
			throw new IndexOutOfBoundsException();	
	}
	
	protected void notifyWrite(int position, int length) {
		notifyChange(new ArrayWriteEvent(this, position, length));
	}
	
	protected void notifyRemove(int position, int length) {
		notifyChange(new ArrayRemoveEvent(this, position, length));
	}
	
	protected void notifyInsert(int position, int length) {
		notifyChange(new ArrayInsertEvent(this, position, length));
	}
}
